#include "asn1.h"
#include "obj_dat.h"
#include "lhash.h"
#include "objects.h"
#include "err.h"
#include "cryptlib.h"
#include "limits.h"

#define ADDED_NID 3
#define ADDED_DATA 0

//#define OBJerr(f,r)  ERR_PUT_error(ERR_LIB_OBJ,(f),(r),__FILE__,__LINE__)

typedef struct added_obj_st {
	int type;
	ASN1_OBJECT *obj;
} ADDED_OBJ;

static LHASH *added = NULL;

void reset_OBJ_nid2ln_reset(void)
{
	added = NULL;
}
/////////////OBJ_nid2ln/////////////////////////ok

const char *OBJ_nid2ln(int n)
{
	ADDED_OBJ ad, *adp;
	ASN1_OBJECT ob;

	if ((n >= 0) && (n < NUM_NID)) {
		if ((n != NID_undef) && (nid_objs[n].nid == NID_undef)) {
			return (NULL);
		}
		return (nid_objs[n].ln);
	} else if (added == NULL)
		return (NULL);
	else {
		ad.type = ADDED_NID;
		ad.obj  = &ob;
		ob.nid  = n;
		adp     = (ADDED_OBJ *)lh_retrieve(added, &ad);
		if (adp != NULL)
			return (adp->obj->ln);
		else {
			return (NULL);
		}
	}
}
///////////////OBJ_bsearch/////////////////ok

const char *OBJ_bsearch(const char *key, const char *base, int num, int size,
			int (*cmp)(const void *, const void *))
{
	return OBJ_bsearch_ex(key, base, num, size, cmp, 0);
}

//////////////////////////////////////////////////////////////////ok

const char *OBJ_bsearch_ex(const char *key, const char *base, int num, int size,
			   int (*cmp)(const void *, const void *), int flags)
{
	int l, h, i = 0, c = 0;
	const char *p = NULL;

	if (num == 0)
		return (NULL);
	l = 0;
	h = num;
	while (l < h) {
		i = (l + h) / 2;
		p = &(base[i * size]);
		c = (*cmp)(key, p);
		if (c < 0)
			h = i;
		else if (c > 0)
			l = i + 1;
		else
			break;
	}
	//#ifdef CHARSET_EBCDIC			//###samyang  modity
	/* THIS IS A KLUDGE - Because the *_obj is sorted in ASCII order, and
 * I don't have perl (yet), we revert to a *LINEAR* search
 * when the object wasn't found in the binary search.
 */
	if (c != 0) {
		for (i = 0; i < num; ++i) {
			p = &(base[i * size]);
			c = (*cmp)(key, p);
			if (c == 0 ||
			    (c < 0 && (flags & OBJ_BSEARCH_VALUE_ON_NOMATCH)))
				return p;
		}
	}
	//#endif		////###samyang  modity
	if (c != 0 && !(flags & OBJ_BSEARCH_VALUE_ON_NOMATCH))
		p = NULL; //&(base[78*size]);
	else if (c == 0 && (flags & OBJ_BSEARCH_FIRST_VALUE_ON_MATCH)) {
		while (i > 0 && (*cmp)(key, &(base[(i - 1) * size])) == 0)
			i--;
		p = &(base[i * size]);
	}
	return (p);
}

//////////////////obj_cmp//////////////////ok

static int obj_cmp(const void *ap, const void *bp)
{
	int j;
	const ASN1_OBJECT *a = *(ASN1_OBJECT * const *)ap;
	const ASN1_OBJECT *b = *(ASN1_OBJECT * const *)bp;

	j = (a->length - b->length);
	if (j)
		return (j);
	return (memcmp(a->data, b->data, a->length));
}
///////////////////OBJ_obj2nid//////////////////////ok

int OBJ_obj2nid(const ASN1_OBJECT *a)
{
	ASN1_OBJECT **op;
	ADDED_OBJ ad, *adp;

	if (a == NULL)
		return (NID_undef);
	if (a->nid != 0)
		return (a->nid);

	if (added != NULL) {
		ad.type = ADDED_DATA;
		ad.obj  = (ASN1_OBJECT *)a; /* XXX: ugly but harmless */
		adp     = (ADDED_OBJ *)lh_retrieve(added, &ad);
		if (adp != NULL)
			return (adp->obj->nid);
	}
	op = (ASN1_OBJECT **)OBJ_bsearch((const char *)&a,
					 (const char *)obj_objs, NUM_OBJ,
					 sizeof(ASN1_OBJECT *), obj_cmp);
	if (op == NULL)
		return (NID_undef);
	return ((*op)->nid);
}

///////////////OBJ_nid2obj////////////////////////////////ok

ASN1_OBJECT *OBJ_nid2obj(int n)
{
	ADDED_OBJ ad, *adp;
	ASN1_OBJECT ob;

	if ((n >= 0) && (n < NUM_NID)) {
		if ((n != NID_undef) && (nid_objs[n].nid == NID_undef)) {
			return (NULL);
		}
		return ((ASN1_OBJECT *)&(nid_objs[n]));
	} else if (added == NULL)
		return (NULL);
	else {
		ad.type = ADDED_NID;
		ad.obj  = &ob;
		ob.nid  = n;
		adp     = (ADDED_OBJ *)lh_retrieve(added, &ad);
		if (adp != NULL)
			return (adp->obj);
		else {
			return (NULL);
		}
	}
}

const char *OBJ_nid2sn(int n)
{
	ADDED_OBJ ad, *adp;
	ASN1_OBJECT ob;

	if ((n >= 0) && (n < NUM_NID)) {
		if ((n != NID_undef) && (nid_objs[n].nid == NID_undef)) {
			OBJerr(OBJ_F_OBJ_NID2SN, OBJ_R_UNKNOWN_NID);
			return (NULL);
		}
		return (nid_objs[n].sn);
	} else if (added == NULL)
		return (NULL);
	else {
		ad.type = ADDED_NID;
		ad.obj  = &ob;
		ob.nid  = n;
		adp     = lh_retrieve(added, &ad);
		if (adp != NULL)
			return (adp->obj->sn);
		else {
			return (NULL);
		}
	}
}

int OBJ_obj2txt(char *buf, int buf_len, const ASN1_OBJECT *a, int no_name)
{
	//	int i,n=0,len,nid, first, use_bn;
	//	BIGNUM *bl;
	//	unsigned long l;
	//	const unsigned char *p;
	//	char tbuf[DECIMAL_SIZE(i)+DECIMAL_SIZE(l)+2];
	//
	//	if ((a == NULL) || (a->data == NULL)) {
	//		buf[0]='\0';
	//		return(0);
	//	}
	//
	//	if (!no_name && (nid=OBJ_obj2nid(a)) != NID_undef)
	//		{
	//		const char *s;
	//		s=OBJ_nid2ln(nid);
	//		if (s == NULL)
	//			s=OBJ_nid2sn(nid);
	//		if (s)
	//			{
	//			if (buf)
	//				strncpy(buf,s,buf_len);
	//			n=strlen(s);
	//			return n;
	//			}
	//		}
	//
	//	len=a->length;
	//	p=a->data;
	//
	//	first = 1;
	//	bl = NULL;
	//
	//	while (len > 0)
	//		{
	//		l=0;
	//		use_bn = 0;
	//		for (;;)
	//			{
	//			unsigned char c = *p++;
	//			len--;
	//			if ((len == 0) && (c & 0x80))
	//				goto err;
	//			if (use_bn)
	//				{
	//				if (!BN_add_word(bl, c & 0x7f))
	//					goto err;
	//				}
	//			else
	//				l |= c  & 0x7f;
	//			if (!(c & 0x80))
	//				break;
	//			if (!use_bn && (l > (ULONG_MAX >> 7L)))
	//				{
	//				if (!bl && !(bl = BN_new()))
	//					goto err;
	//				if (!BN_set_word(bl, l))
	//					goto err;
	//				use_bn = 1;
	//				}
	//			if (use_bn)
	//				{
	//				if (!BN_lshift(bl, bl, 7))
	//					goto err;
	//				}
	//			else
	//				l<<=7L;
	//			}
	//
	//		if (first)
	//			{
	//			first = 0;
	//			if (l >= 80)
	//				{
	//				i = 2;
	//				if (use_bn)
	//					{
	//					if (!BN_sub_word(bl, 80))
	//						goto err;
	//					}
	//				else
	//					l -= 80;
	//				}
	//			else
	//				{
	//				i=(int)(l/40);
	//				l-=(long)(i*40);
	//				}
	//			if (buf && (buf_len > 0))
	//				{
	//				*buf++ = i + '0';
	//				buf_len--;
	//				}
	//			n++;
	//			}
	//
	//		if (use_bn)
	//			{
	//			char *bndec;
	//			bndec = BN_bn2dec(bl);
	//			if (!bndec)
	//				goto err;
	//			i = strlen(bndec);
	//			if (buf)
	//				{
	//				if (buf_len > 0)
	//					{
	//					*buf++ = '.';
	//					buf_len--;
	//					}
	//				strncpy(buf,bndec,buf_len);
	//				if (i > buf_len)
	//					{
	//					buf += buf_len;
	//					buf_len = 0;
	//					}
	//				else
	//					{
	//					buf+=i;
	//					buf_len-=i;
	//					}
	//				}
	//			n++;
	//			n += i;
	//			OPENSSL_free(bndec);
	//			}
	//		else
	//			{
	//			BIO_snprintf(tbuf,sizeof tbuf,".%lu",l);
	//			i=strlen(tbuf);
	//			if (buf && (buf_len > 0))
	//				{
	//				strncpy(buf,tbuf,buf_len);
	//				if (i > buf_len)
	//					{
	//					buf += buf_len;
	//					buf_len = 0;
	//					}
	//				else
	//					{
	//					buf+=i;
	//					buf_len-=i;
	//					}
	//				}
	//			n+=i;
	//			l=0;
	//			}
	//		}
	//
	//	if (bl)
	//		BN_free(bl);
	//	return n;
	//
	//	err:
	//	if (bl)
	//		BN_free(bl);
	return -1;
}

int OBJ_obj2name(char *dst_buf, int buf_len, const ASN1_OBJECT *a)
{
	if (buf_len < a->length) {
		printf("OBJ_obj2name err: not enough buffer to store name\n");

		return -1;
	}
	memcpy(dst_buf, a->data, a->length);

	return a->length;
}
